package ctp.statesgraph;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import ctp.ctpgraph.CTPGraph;
import ctp.ctpgraph.Edge;
import ctp.ctpgraph.Vertex;
import ctp.utils.GraphUtils;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.Tree;
import edu.uci.ics.jung.graph.util.TreeUtils;

public class Policy {
	
	Map<State, PolicyAction> stateActionMap;
	State initialState;
	
	public static boolean isPolicyAllVerticesCommiting(Policy policy, CTPGraph ctpGraph) {
		for (Vertex vertex : ctpGraph.getGraph().getVertices()) {
			if (!isVertexCommiting(policy, vertex, ctpGraph)) {
				return false;
			}
		}
		
		return true;
	}

	private static boolean isVertexCommiting(Policy policy, Vertex vertex, CTPGraph ctpGraph) {
		if (vertex.isGoal(ctpGraph) || ctpGraph.getTree().isLeaf(vertex) || ctpGraph.getTree().isRoot(vertex)) {
			return true;
		}

		for (Entry<State,PolicyAction> entry : policy.stateActionMap.entrySet()) {

			State st = entry.getKey();
			if (st.isGoalState(ctpGraph) || st instanceof ObservationState) {
				continue;
			}
			// Checking if the policy's state is in the subtree.
			if (ctpGraph.getTree().getSuccessors(vertex).contains(st.getLocation()) || vertex == st.getLocation()) {
				if(!isVertexStateActionCommiting(st, entry.getValue().getAction(), vertex, ctpGraph)) {
					return false;
				}
				
			}
		}

		return true;
	}
	
	public static boolean isVertexStateActionCommiting(State state, Action action, Vertex vertex, CTPGraph ctpGraph) {
		if (vertex.isGoal(ctpGraph)) {
			return true;
		}
		if (ctpGraph.getTree().isRoot(vertex)) {
			return true;
		}
		Edge parentEdge = ctpGraph.getTree().getParentEdge(vertex);	
		
		List<Edge> path = action.getPath();
		/*
		 * if action.path contains the parent edge and in the right direction (so we are in the subtree and
		 * leaving it) then: we change the cost of the parent edge to a very high level H and run dijekstra on
		 * the graph as obtained from the state and the cost is greater than H continue (else return false).
		 */
		
		if (path.contains(parentEdge)) {
			// Need to check that the location in the state is contained in the subtree of vertex.
			try {
				Tree<Vertex, Edge> subtree = TreeUtils.getSubTree(ctpGraph.getTree(), vertex);
				if (subtree.containsVertex(state.location)) {
					double originalCost = parentEdge.getCost();
					parentEdge.setCost(ctpGraph.infinityCost());
					Graph<Vertex, Edge> kernel = GraphUtils.buildKernel(ctpGraph, state, false);
					if (!kernel.containsVertex(ctpGraph.getGoal())) {
						throw new RuntimeException("kernel does not contain goal vertex!");
					}
					double pathCost = GraphUtils.dijkstraOnGraph(kernel, vertex, ctpGraph.getGoal());
					parentEdge.setCost(originalCost);
					if (pathCost < ctpGraph.infinityCost()) {
						return false;
					}
				}
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
			
		}
		return true;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((initialState == null) ? 0 : initialState.hashCode());
		result = prime * result
				+ ((stateActionMap == null) ? 0 : stateActionMap.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Policy other = (Policy) obj;
		if (initialState == null) {
			if (other.initialState != null)
				return false;
		} else if (!initialState.equals(other.initialState))
			return false;
		if (stateActionMap == null) {
			if (other.stateActionMap != null)
				return false;
		} else if (!stateActionMap.equals(other.stateActionMap))
			return false;
		return true;
	}

	public Policy() {
		super();
		this.stateActionMap = new HashMap<State, PolicyAction>();
	}

	public Map<State, PolicyAction> getStateActionMap() {
		return stateActionMap;
	}
	
	public double getCost() {
		return this.stateActionMap.get(this.initialState).cost;
	}
	
	public void print(boolean printObservationState) {
		for (Entry<State, PolicyAction> entry : stateActionMap.entrySet()) {
			State st = entry.getKey();
			PolicyAction action = entry.getValue();
			if (st instanceof ActionState) {
				if (isInitialState(st)) {
					System.out.print("--> ");
				}
				//System.out.println(((ActionState) st).print() + " " + action.toString());
				System.out.println(st.getId() + " : " + ((ActionState) st).print() + " " + action.toString());
			} else if (printObservationState) {
				System.out.println(st.getId() + " : " + ((ObservationState) st).print());
			}
		}
	}
	
	public void setInitialState(State st) {
		this.initialState = st;
	}
	
	public State getInitialState() {
		return this.initialState;
	}
	
	public boolean isInitialState(State st) {
		return this.initialState == st;
	}

	public void printInitState() {
		System.out.println(String.format("initial state: %s %s", this.initialState, this.stateActionMap.get(this.initialState)));
	}

}
